package com.sky.business.sys.handler.cmcc;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sky.business.sys.domain.CameraConfig;
import com.sky.business.sys.domain.SystemConfig;
import com.sky.business.sys.domain.TrackTask;
import com.sky.business.sys.event.CmccBeginTrackingEvent;
import com.sky.business.sys.event.EdgeBeginTrackingEvent;
import com.sky.business.sys.event.TrackDispatchStatusEvent;
import com.sky.business.sys.handler.MqttEventHandler;
import com.sky.business.sys.service.CameraConfigService;
import com.sky.business.sys.service.DispatcherService;
import com.sky.business.sys.service.SystemConfigService;
import com.sky.business.sys.service.TrackTaskService;
import com.sky.business.sys.util.MQTTClientUtils;
import com.sky.common.endpoint.CameraSelector;
import com.sky.common.config.CacheConstants;
import com.sky.common.enums.DispatchType;
import com.sky.common.track.DeviceStatus;
import com.sky.common.track.TrackTaskInfo;
import com.sky.common.track.TrackTaskManager;
import com.sky.common.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 移动凌云平台开始跟踪任务处理器
 *
 */
@Slf4j
@Component
public class CmccBeginTrackingEventHandler implements MqttEventHandler {

    public static ObjectMapper objectMapper = new ObjectMapper().setTimeZone(SimpleTimeZone.getTimeZone("GMT+8"));

    @Autowired
    private CameraSelector cameraSelector;

    @Autowired
    private CameraConfigService cameraConfigService;

    @Autowired
    private TrackTaskService trackTaskService;

    @Autowired
    private SystemConfigService systemConfigService;

    @Autowired
    private DispatcherService dispatcherService;

    @Override
    public boolean match(String topic, String msg, String event) {
        return "BeginTracking".equals(event);
    }


    /**
     * 获取不存在的设备id列表
     *
     * @param deviceList       设备id列表
     * @param cameraConfigList 设备信息记录列表
     * @return 不存在的设备id列表
     */
    public List<String> getNotFoundDeviceList(List<String> deviceList, List<CameraConfig> cameraConfigList) {
        Set<String> deviceIdSet = new HashSet<>(deviceList);
        Set<String> existDeviceIdSet = cameraConfigList.stream().map(CameraConfig::getCameraDeviceId).collect(Collectors.toSet());
        deviceIdSet.removeAll(existDeviceIdSet);
        return new ArrayList<>(deviceIdSet);
    }


    /**
     * 转发开始跟踪消息给终端
     *
     * @param edgeId       边缘中心id
     * @param cameraConfig 设备配置记录
     * @param metadata     元数据
     */
    public void forwardBeginTracking(String edgeId, CameraConfig cameraConfig, CmccBeginTrackingEvent.Metadata metadata) {
        try {
            EdgeBeginTrackingEvent edgeBeginTrackingEvent = new EdgeBeginTrackingEvent(edgeId, cameraConfig.getCameraDeviceId(), metadata.getTaskId(), metadata.getObjectData(), metadata.getAiData(), metadata.getExtention());
            String msg = objectMapper.writeValueAsString(edgeBeginTrackingEvent);
            MQTTClientUtils.publish(MQTTClientUtils.LOCAL_MQTT_CLIENT, cameraConfig.getDispatcherTopic(), CacheConstants.DEFAULT_QOS, msg);
        } catch (Exception e) {
            log.error("转发开始跟踪消息发生异常, 任务ID:{}, 设备ID:{}", metadata.getTaskId(), cameraConfig.getCameraDeviceId(), e);
        }
    }



    /**
     * 上报追踪任务分配状态
     *
     * @param systemConfig            系统配置
     * @param taskId                  任务id
     * @param deviceIdList            强制指定设备列表
     * @param allowDeviceIdList       允许选择设备列表
     * @param notFoundDeviceIdList    未找到设备列表
     * @param noHeartbeatDeviceIdList 没有心跳设备列表
     * @param busyDeviceIdList        被占用设备列表
     * @param trackingDeviceIdList    选择设备列表
     */
    public void reportTrackStatus(SystemConfig systemConfig, String taskId, List<String> deviceIdList, List<String> allowDeviceIdList, List<String> notFoundDeviceIdList, List<String> noHeartbeatDeviceIdList, List<String> busyDeviceIdList, List<String> trackingDeviceIdList) {
        try {
            TrackDispatchStatusEvent trackDispatchStatusEvent = new TrackDispatchStatusEvent(systemConfig.getEdgeId(), taskId, deviceIdList, allowDeviceIdList, notFoundDeviceIdList, noHeartbeatDeviceIdList, busyDeviceIdList, trackingDeviceIdList);
            String responseMsg = objectMapper.writeValueAsString(trackDispatchStatusEvent);
            MQTTClientUtils.publish(MQTTClientUtils.CMCC_MQTT_CLIENT, systemConfig.getCmccReportingTopic(), CacheConstants.DEFAULT_QOS, responseMsg);
        } catch (Exception e) {
            log.error("上报追踪任务分配状态发生异常,任务ID:{}", taskId, e);
        }
    }


    /**
     * 处理cmcc指定设备id的开始跟踪事件
     *
     * @param cmccBeginTrackingEvent cmcc开始跟踪事件
     */
    public void beginTrackingUsingCmcc(CmccBeginTrackingEvent cmccBeginTrackingEvent) {
        Date now = new Date();
        Date expireHeartbeatDate = DateUtils.from(LocalDateTime.now().plusSeconds(-CacheConstants.CAMERA_HEARTBEAT_TIMEOUT_SECOND));
        CmccBeginTrackingEvent.Metadata metadata = cmccBeginTrackingEvent.getMetadata();
        List<String> deviceIdList = metadata.getDeviceIds();
        SystemConfig systemConfig = systemConfigService.getSystemConfig();
        List<CameraConfig> cameraConfigList = cameraConfigService.findByDeviceIdIn(metadata.getDeviceIds());
        List<String> noHeartbeatDeviceIdList = new ArrayList<>();
        List<String> trackingDeviceIdList = new ArrayList<>();
        List<CameraConfig> heartbeatCameraConfigList = new ArrayList<>();
        List<String> notFoundDeviceIdList = getNotFoundDeviceList(deviceIdList, cameraConfigList);
        List<String> busyDeviceIdList = new ArrayList<>();

        if (cameraConfigList.isEmpty()) {
            //上报追踪任务分配状态
            reportTrackStatus(systemConfig, metadata.getTaskId(), metadata.getDeviceIds(), metadata.getAllowDeviceIds(), notFoundDeviceIdList, noHeartbeatDeviceIdList, busyDeviceIdList, trackingDeviceIdList);
            return;
        }
        //查询没有心跳的设备
        for (CameraConfig cameraConfig : cameraConfigList) {
            DeviceStatus deviceStatus = TrackTaskManager.getDeviceStatus(cameraConfig.getCameraDeviceId());
            if (deviceStatus.getLastHeartbeat() == null || deviceStatus.getLastHeartbeat().before(expireHeartbeatDate)) {
                //没有心跳
                noHeartbeatDeviceIdList.add(cameraConfig.getCameraDeviceId());
            } else {
                heartbeatCameraConfigList.add(cameraConfig);
            }
        }

        //都没有心跳
        if (heartbeatCameraConfigList.isEmpty()) {
            //上报追踪任务分配状态
            reportTrackStatus(systemConfig, metadata.getTaskId(), metadata.getDeviceIds(), metadata.getAllowDeviceIds(), notFoundDeviceIdList, noHeartbeatDeviceIdList, busyDeviceIdList, trackingDeviceIdList);
            return;
        }

        trackingDeviceIdList = heartbeatCameraConfigList.stream().map(CameraConfig::getCameraDeviceId).collect(Collectors.toList());

        //停止设备列表以外的其他设备对本任务的跟踪
        dispatcherService.stopOtherDeviceTracking(metadata.getTaskId(), deviceIdList);

        //停止列表中的设备对其他 任务的跟踪
        List<TrackTask> discardTaskList = trackTaskService.findNoCmccTaskByTaskIdNotEqualsAndDeviceIdIn(metadata.getTaskId(), metadata.getDeviceIds());
        dispatcherService.endTracking(discardTaskList);

        //入库
        for (CameraConfig cameraConfig : heartbeatCameraConfigList) {
            List<TrackTask> trackTaskList = trackTaskService.findByTaskIdAndDeviceId(metadata.getTaskId(), cameraConfig.getCameraDeviceId());
            if (trackTaskList.size() > 0) {
                TrackTask trackTask = trackTaskList.get(0);
                if (trackTask.getDispatchType() != DispatchType.CMCC) {
                    trackTask.setDispatchType(DispatchType.CMCC);
                    trackTaskService.updateById(trackTask);
                }
                for (int i = 1; i < trackTaskList.size(); i++) {
                    trackTaskService.endTrack(trackTaskList.get(i), now);
                }
            } else {
                trackTaskService.create(cameraConfig.getCameraDeviceId(), metadata.getTaskId(), DispatchType.CMCC);
            }
            //转发开始跟踪消息给终端
            forwardBeginTracking(systemConfig.getEdgeId(), cameraConfig, metadata);
        }
        //上报追踪任务分配状态
        reportTrackStatus(systemConfig, metadata.getTaskId(), metadata.getDeviceIds(), metadata.getAllowDeviceIds(), notFoundDeviceIdList, noHeartbeatDeviceIdList, busyDeviceIdList, trackingDeviceIdList);
    }

    /**
     * 处理cmcc未指定设备id的开始跟踪事件
     *
     * @param cmccBeginTrackingEvent cmcc开始跟踪事件
     */
    public void beginTrackingUsingEdge(CmccBeginTrackingEvent cmccBeginTrackingEvent) {
        Date expireHeartbeatDate = DateUtils.from(LocalDateTime.now().plusSeconds(-CacheConstants.CAMERA_HEARTBEAT_TIMEOUT_SECOND));
        SystemConfig systemConfig = systemConfigService.getSystemConfig();
        CmccBeginTrackingEvent.Metadata metadata = cmccBeginTrackingEvent.getMetadata();
        List<String> allowDeviceIdList = metadata.getAllowDeviceIds();
        //查询设备是否存在
        List<CameraConfig> cameraConfigList = cameraConfigService.findByDeviceIdIn(allowDeviceIdList);
        List<String> noHeartbeatDeviceList = new ArrayList<>();
        List<CameraConfig> heartbeatCameraList = new ArrayList<>();
        List<String> trackingDeviceList = new ArrayList<>();
        List<String> notFoundDeviceList = getNotFoundDeviceList(allowDeviceIdList, cameraConfigList);
        List<String> busyDeviceList = new ArrayList<>();

        //设备全部不存在
        if (cameraConfigList.isEmpty()) {
            //上报追踪任务分配状态
            reportTrackStatus(systemConfig, metadata.getTaskId(), metadata.getDeviceIds(), metadata.getAllowDeviceIds(), notFoundDeviceList, noHeartbeatDeviceList, busyDeviceList, trackingDeviceList);
            return;
        }

        //查询心跳
        for (CameraConfig cameraConfig : cameraConfigList) {
            DeviceStatus deviceStatus = TrackTaskManager.getDeviceStatus(cameraConfig.getCameraDeviceId());
            if (deviceStatus.getLastHeartbeat() == null || deviceStatus.getLastHeartbeat().before(expireHeartbeatDate)) {
                //没有心跳
                noHeartbeatDeviceList.add(cameraConfig.getCameraDeviceId());
            } else {
                //有心跳
                heartbeatCameraList.add(cameraConfig);
            }
        }

        //所有备选设备都没有心跳
        if (heartbeatCameraList.isEmpty()) {
            //上报追踪任务分配状态
            reportTrackStatus(systemConfig, metadata.getTaskId(), metadata.getDeviceIds(), metadata.getAllowDeviceIds(), notFoundDeviceList, noHeartbeatDeviceList, busyDeviceList, trackingDeviceList);
            return;
        }

        //停止设备列表以外的其他设备对本任务的跟踪
        dispatcherService.stopOtherDeviceTracking(metadata.getTaskId(), allowDeviceIdList);

        //查询执行追踪任务的所有设备信息
        List<TrackTask> trackTaskList = trackTaskService.findByTaskIdAndDeviceIdIn(metadata.getTaskId(), heartbeatCameraList.stream().map(CameraConfig::getCameraDeviceId).collect(Collectors.toList()));
        Set<String> incapableDeviceIdSet = new HashSet<>();
        if (trackTaskList.size() > 0) {
            //查询设备和追踪任务信息
            Map<String,TrackTask > deviceTrackTaskMap = new HashMap<>();
            for (TrackTask trackTask : trackTaskList) {
                if (trackTask.getDispatchType() == DispatchType.CMCC) {
                    trackTask.setDispatchType(DispatchType.EDGE_PRIMARY);
                    trackTaskService.updateById(trackTask);
                }
                deviceTrackTaskMap.put(trackTask.getDeviceId(),trackTask);
            }
            //计算跟踪丢失的临界值
            Date lossTime = DateUtils.from(LocalDateTime.now().plusSeconds(-CacheConstants.TRACKING_LOSS_THRESHOLD_SECONDS));
            TrackTaskInfo trackTaskInfo = TrackTaskManager.getTrackTaskInfo(metadata.getTaskId());
            Map<String, Date> lastReceiveMap = trackTaskInfo != null ? trackTaskInfo.getDeviceLastReceiveMap() : null;

            //判断执行追踪任务的设备是否具有继续追踪能力
            for (String deviceId : deviceTrackTaskMap.keySet()) {
                TrackTask trackTask = deviceTrackTaskMap.get(deviceId);
                long  passSeconds = DateUtils.secondPlus(new Date(), trackTask.getBeginTime());
                if(passSeconds>=CacheConstants.TRACKING_LOSS_THRESHOLD_SECONDS) {
                    Date lastReceiveDate = lastReceiveMap != null ? lastReceiveMap.get(deviceId) : null;
                    if (lastReceiveDate == null ||  lastReceiveDate.before(lossTime)) {
                        //一段时间未追踪到无人机
                        log.warn("无人机连续{}秒追踪丢失，结束追踪。 设备ID:{}, 任务ID:{}", CacheConstants.TRACKING_LOSS_THRESHOLD_SECONDS, deviceId, metadata.getTaskId());
                        incapableDeviceIdSet.add(deviceId);
                        //结束没有跟踪能力的设备
                        dispatcherService.endTracking(metadata.getTaskId(), deviceId);
                        continue;
                    }
                }
                CameraConfig cameraConfig = cameraConfigService.findByDeviceId(deviceId);
                //判断是否在最大追踪范围内
                boolean inDetect = dispatcherService.inTrackingRange(cameraConfig, metadata.getObjectData().getLongitude().doubleValue(), metadata.getObjectData().getLatitude().doubleValue(), metadata.getObjectData().getAltitude().doubleValue());
                if (!inDetect) {
                    log.warn("无人机超出设备追踪范围，结束追踪。 设备ID:{}, 任务ID:{}",deviceId,metadata.getTaskId());
                    incapableDeviceIdSet.add(deviceId);
                    //结束没有跟踪能力的设备
                    dispatcherService.endTracking(metadata.getTaskId(),deviceId);
                    continue;
                }
                //放到跟踪设备列表
                trackingDeviceList.add(cameraConfig.getCameraDeviceId());
                //转发mqtt消息
                forwardBeginTracking(systemConfig.getEdgeId(), cameraConfig, metadata);
            }
        }
        if(trackingDeviceList.isEmpty()){
            //查询空闲的设备
            List<TrackTask> otherTaskList = trackTaskService.findByTaskIdNotEqualsAndDeviceIdIn(metadata.getTaskId(), heartbeatCameraList.stream().map(CameraConfig::getCameraDeviceId).collect(Collectors.toList()));
            Set<String> busyDeviceIdSet = otherTaskList.stream().map(TrackTask::getDeviceId).collect(Collectors.toSet());
            List<CameraConfig> idleCameraConfigList = new ArrayList<>();
            for (CameraConfig cameraConfig : heartbeatCameraList) {
                if(incapableDeviceIdSet.contains(cameraConfig.getCameraDeviceId())){
                    continue;
                }
                if (busyDeviceIdSet.contains(cameraConfig.getCameraDeviceId())) {
                    busyDeviceList.add(cameraConfig.getCameraDeviceId());
                } else {
                    idleCameraConfigList.add(cameraConfig);
                }
            }
            //没有空闲设备
            if (idleCameraConfigList.isEmpty()) {
                //上报追踪任务分配状态
                reportTrackStatus(systemConfig, metadata.getTaskId(), metadata.getDeviceIds(), metadata.getAllowDeviceIds(), notFoundDeviceList, noHeartbeatDeviceList, busyDeviceList, trackingDeviceList);
                return;
            }
            //选择可以跟踪的设备
            List<CameraConfig> trackingAbleCameraConfigList = cameraSelector.selectTrackDevice(idleCameraConfigList, metadata.getObjectData().getLongitude().doubleValue(), metadata.getObjectData().getLatitude().doubleValue(), metadata.getObjectData().getAltitude().doubleValue());
            if(trackingAbleCameraConfigList.size()>0) {
                List<CameraConfig> notInOeTaskCameraConfig = new ArrayList<>();
                //找出自主跟踪的设备列表
                for (CameraConfig currCameraConfig : trackingAbleCameraConfigList) {
                    //判断设备是否在自主跟踪无人机
                    boolean inOeTask = TrackTaskManager.inOeTask(currCameraConfig.getCameraDeviceId());
                    if (!inOeTask) {
                        notInOeTaskCameraConfig.add(currCameraConfig);
                    }
                }
                CameraConfig trackingcameraConfig;
                if (notInOeTaskCameraConfig.size() > 0) {
                    trackingcameraConfig = notInOeTaskCameraConfig.get(0);
                } else {
                    trackingcameraConfig = trackingAbleCameraConfigList.get(0);
                }
                trackingDeviceList.add(trackingcameraConfig.getCameraDeviceId());
                trackTaskService.create(trackingcameraConfig.getCameraDeviceId(), metadata.getTaskId(), DispatchType.EDGE_PRIMARY);
                //转发
                forwardBeginTracking(systemConfig.getEdgeId(), trackingcameraConfig, metadata);
            }else{
                //未找到合格的跟踪设备
                log.warn("追踪任务未找到合格设备,任务ID:{}",metadata.getTaskId());
            }
        }
        //上报追踪任务分配状态
        reportTrackStatus(systemConfig, metadata.getTaskId(), metadata.getDeviceIds(), metadata.getAllowDeviceIds(), notFoundDeviceList, noHeartbeatDeviceList, busyDeviceList, trackingDeviceList);
    }


    @Override
    public void handle(String topic, String msg) throws Exception {
        CmccBeginTrackingEvent cmccBeginTrackingEvent = objectMapper.readValue(msg, CmccBeginTrackingEvent.class);
        CmccBeginTrackingEvent.Metadata metadata = cmccBeginTrackingEvent.getMetadata();
        List<String> deviceIdList = cmccBeginTrackingEvent.getMetadata().getDeviceIds();
        List<String> allowDeviceIdList = cmccBeginTrackingEvent.getMetadata().getAllowDeviceIds();
        //记录任务下发时间
        TrackTaskManager.updateReceiveTrackTime(metadata.getTaskId(), new Date(), cmccBeginTrackingEvent);
        //判断追踪任务类型
        if (deviceIdList != null && deviceIdList.size() > 0) {
            //凌云直接调度
            beginTrackingUsingCmcc(cmccBeginTrackingEvent);
        } else if (allowDeviceIdList != null && allowDeviceIdList.size() > 0) {
            //凌云划定设备范围,由边缘中心选择
            beginTrackingUsingEdge(cmccBeginTrackingEvent);
        } else {
            //格式错误,deviceIds和allowDeviceIds都没有设备
            log.error("凌云平台下发开始跟踪消息格式错误:{}", msg);
        }
    }
}
